package com.effectivsocial.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import lombok.extern.slf4j.Slf4j;

import org.joda.time.DateTime;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.authentication.encoding.PasswordEncoder;

import com.effectivsocial.domain.Company;
import com.effectivsocial.domain.Role;
import com.effectivsocial.domain.User;
import com.effectivsocial.repository.CompanyRepository;
import com.effectivsocial.repository.LovRepository;
import com.effectivsocial.repository.UserRepository;
import com.effectivsocial.service.api.AbstractService;
import com.effectivsocial.service.api.UserService;
import com.effectivsocial.service.exception.MissingUserException;
import com.effectivsocial.service.security.SimpleUserDetails;

@Named
@Slf4j
public class UserServiceImpl extends AbstractService<User, String> implements UserService{
	
	@Inject
	private UserRepository repository;
	
	@Inject
	private LovRepository lovRepository;
	
	@Inject
	private CompanyRepository companyRepository;
	
	@Inject
    private PasswordEncoder encoder;

	
	public List<User> findByDeleted(Pageable pageable, boolean deleted) {
		return repository.findByDeleted(pageable, deleted);
	}
	
	public User findByEmail(String email) {
		return repository.findByEmail(email);
	}
	
	@Override
	protected void initInternal() {
		setRepository(repository);
	}

	public List<User> findByRole(Role role) {
		return repository.findByRoles(role);
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void save(User user, Role role) {
		
		//first save company
		Company company = user.getCompany();
		
		if(company != null) {
			includeAuditTimestamp(company);
			companyRepository.save(company);
			user.setCompany(company);
		}
		
		
		//first save user
		includeAuditTimestamp(user);
		user.setPassword(encoder.encodePassword(user.getPassword(), null));

		repository.save(user);
		
		//retrieve role
		Role attachedRole = (Role) lovRepository.findByTypeAndName(role.getType(), role.getName());
		
		List<Role> roles = user.getRoles();
		
		if(roles == null) {
			roles = new ArrayList<Role>();
		}
		
		roles.add(attachedRole);
		user.setRoles(roles);
		
		update(user);
	}
	@Transactional(propagation=Propagation.REQUIRED)
	public void update(User user) {
		includeAuditTimestampUpdate(user);
		
	}

	@Transactional(readOnly=true)
	public UserDetails loadUserByUsername(String userName)
			throws UsernameNotFoundException {
		User user = null;
		SimpleUserDetails userDetails = null;
		try{
			user = repository.findByEmail(userName);
			
			log.debug("Retrieved user - {},",user);
			
			if(user == null) {
				throw new UsernameNotFoundException("Username -" + userName + " not found.");
			}
			
			List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority> ();
			
			List<Role> userRoles = user.getRoles();
			
			log.debug("Retrieved roles - {},",userRoles);
			
			for(Role role : userRoles) {
				authorities.add(new SimpleGrantedAuthority(role.getName()));
			}
		
			log.debug("Retrieved authorities - {},",authorities);	
			
			userDetails = new SimpleUserDetails(user, authorities);
			
			
		}
		catch(Exception er){
			
			log.error("Error retrieving user - ", er);
			throw new UsernameNotFoundException("Error retrieving user - " + er.getMessage());
		}
		
		
		return userDetails;
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void save(User user, Company company) {
		
		user.setCompany(company);
		user.setLocked(true);
		//TODO - set locked to true
		Role role = new Role();
		role.setType("Role");
		role.setName("ROLE_USER");
		
		save(user, role);
		
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void unlock(String email) {
		User user = repository.findByEmail(email);
		if(user == null){
			throw new MissingUserException("No user record found for email : " + email);
		}
		
		user.setActivationDate(DateTime.now());
		user.setLocked(false);
		includeAuditTimestampUpdate(user);
	}

}
